import * as config from "config"
import * as crypto from 'crypto'
import {Types} from "mongoose";
import * as redis from 'redis'
import {InstanceType} from 'typegoose'
import GM, {generateHash} from "../database/models/gm";
import {KickbackRecordModel} from "../database/models/kickbackRecord";
import {Product, ProductModel} from "../database/models/Product";
import {ProductOrderModel} from "../database/models/ProductOrder";
import {RechargeOrderModel} from "../database/models/rechargeOrder";

export const pcUrl = config.get<string>("otherGame.pc_url")
export const FANGKA_2_COIN = config.get<number>("otherGame.fangKa_2_coin")
export const freeCoinDefault = config.get<number>("player.freeCoinDefault")
export const freeCoinByInviter = config.get<number>("player.freeCoinByInviter")
export const freeCoinBySharer = config.get<number>("player.freeCoinBySharer")
export const freeCoinBySharing = config.get<number>("player.freeCoinBySharing")

export const lipstickResumeTimes = config.get<number>("game.lipstickResumeTimes")

const redisClient = redis.createClient()

interface redisData {
  name: string,
  from: string,
  info: string,
  createAt: Date
}

export const CLIENT_TYPE = {
  NORMAL: `normal`,
  PUCHENG: `pucheng`
}

const delayTime = 10000 //毫秒
let timestamp = 0

export function mkCdKeys(count: number): string[] {
  const randomWord = () => Math.random().toString(36).substr(5).toUpperCase()

  function mkRandomWord() {
    const words = ['0', 'O', 'o', '1', 'l']

    function haveWords(str) {
      for (const it of words) {
        if (str.indexOf(it) > -1 || str.length !== 8)
          return true
      }
      return false
    }

    let k = randomWord()
    while (haveWords(k)) {
      k = randomWord()
    }
    return k
  }

  const ary = []
  for (let i = 0; i < count; i++) {
    let t = mkRandomWord()
    while (ary.indexOf(t) > -1)
      t = mkRandomWord()
    ary.push(t)
  }
  return ary
}

export async function kickBackAll(fromId: Types.ObjectId, type: string) {
  let p2GmKickback = 0
  let gm2GmKickback = 0

  let gm = null
  let RMBCost = null
  let player = null

  if (type === `recharge`) {
    const ro = await RechargeOrderModel.findById(fromId).populate(`player`).populate(`recharge`).lean()

    if (!ro || !ro.player)
      return // 无邀请人
    if (ro.recharge.coin > 0 && ro.recharge.gem === 0) {
      p2GmKickback = config.get<number>('kickback.coin.playerToGmKickback')
      gm2GmKickback = config.get<number>('kickback.coin.gmToGmKickback')
    } else if (ro.recharge.gem > 0 && ro.recharge.coin === 0) {
      p2GmKickback = config.get<number>('kickback.gem.playerToGmKickback')
      gm2GmKickback = config.get<number>('kickback.gem.gmToGmKickback')
    } else {
      console.log(`kickback recharge : unkown recharge type`)
      return
    }
    RMBCost = ro.RMBCost
    player = ro.player
    gm = await GM.findById(ro.inviteBy).lean()
  } else if (type === `buy`) {
    const po = await ProductOrderModel.findById(fromId).populate(`player`).populate(`product`).lean()
    if (!po || !po.player || !po.player.inviteBy)
      return // 无邀请人

    p2GmKickback = config.get<number>('kickback.buy.playerToGmKickback')
    gm2GmKickback = config.get<number>('kickback.buy.gmToGmKickback')

    RMBCost = po.RMBCost
    player = po.player
    gm = await GM.findById(po.inviteBy).lean()
  } else
    throw Error(`error kickback type`)

  if (!gm)
    return // 无合法GM

  const kicks = {level2: null, kickback2: 0, level1: null, kickback1: 0, super: null, kickbackAll: 0}

  switch (gm.role) {
    case `super`: // super的返利余额用于统计返利总和
      return
    case `level1`: {
      const pBack = RMBCost * p2GmKickback
      const gmLv1 = await GM.findByIdAndUpdate(gm, {$inc: {cashKickback: pBack}}, {new: true})

      kicks.level1 = gmLv1
      kicks.kickback1 = pBack
      kicks.kickbackAll = pBack
    }
      break
    case `level2`: {
      const pBack = RMBCost * p2GmKickback
      const gmLv2 = await GM.findByIdAndUpdate(gm, {$inc: {cashKickback: pBack}}, {new: true})
      if (!gmLv2 || !gmLv2.superior)
        throw Error(`${__filename} error gm.superior`)

      kicks.level2 = gmLv2
      kicks.kickback2 = pBack

      const gmBack = pBack * gm2GmKickback
      const gmLv1 = await GM.findByIdAndUpdate(gmLv2.superior, {$inc: {cashKickback: gmBack}}, {new: true})

      kicks.level1 = gmLv1
      kicks.kickback1 = gmBack
      kicks.kickbackAll = pBack + gmBack
    }
      break
    default:
      throw Error(`error gm.role`)

  }

  const su = await GM.findOneAndUpdate({role: `super`}, {$inc: {cashKickback: kicks.kickbackAll}}, {new: true})
  kicks.super = su

  const kickbackRecord = new KickbackRecordModel({
    player,
    RMBCost,
    type,
    fromId,
    ...kicks
  })
  await kickbackRecord.save()
}

function updateTimestamp() {
  if (timestamp <= Math.floor(Date.now() / 1000)) {
    timestamp = Math.floor((Date.now() + delayTime) / 1000)
  }
}

export async function getBroadcastInfo(name = 'broadcastInfo') {
  if (!name) {
    return []
  }
  if (process.env.NODE_ENV === 'test') {
    name = 'testBroadcastInfo'
  }
  let broadcastData = []
  await new Promise((resolve, reject) => {
    redisClient.smembers(name, (err, reply) => {
      if (err) {
        return reject(false)
      }
      broadcastData = reply
      resolve(true)
    })
  })

  await new Promise((resolve, reject) => {
    redisClient.scard(name, (err, reply) => {
      if (err) {
        return reject(false)
      }
      if (reply >= 1000) {
        redisClient.del(name)
      }
      resolve(true)
    })
  })

  return broadcastData || []
}

export function delRedis(name = '') {
  if (!name) {
    return
  }
  redisClient.del(name)
}

export async function putDataInBroadcastInfo(data: redisData, name = 'broadcastInfo') {
  if (!data) {
    return
  }

  if (process.env.NODE_ENV === 'test') {
    name = 'testBroadcastInfo'
  }

  const exitState = await new Promise((resolve, reject) => {
    redisClient.exists(name, (err, reply) => {
      if (err) {
        return reject(false)
      }
      return resolve(reply)
    })
  })
  redisClient.sadd(name, JSON.stringify(data))

  if (exitState === 0) {
    updateTimestamp()
    redisClient.expireat(name, timestamp)
  }
}

export async function recordReadBroadcastPlayer(playerId: string = '', alreadyReadNum = 0) {
  if (!playerId) {
    return
  }

  redisClient.set(playerId, alreadyReadNum.toString())
  redisClient.expireat(playerId, timestamp)
}

export async function getReadBroadcastPlayer(playerId: string = '') {
  if (!playerId) {
    return
  }
  let result = null

  await new Promise((resolve, reject) => {
    redisClient.get(playerId, (err, reply) => {
      if (err) {
        return reject()
      }
      result = reply
      return resolve(reply)
    })
  })

  return result || '0'
}

export function getMD5(str) {
  const md5sum = crypto.createHash('md5')
  md5sum.update(new Buffer(str))
  return md5sum.digest('hex')

}

export function getSkuId(p: InstanceType<Product>) {
  if (!p.brand || !p.series || !p.name || !p.model)
    return null

  function getNum(md5) {
    return parseInt(md5.slice(-4), 16).toString().slice(-3)
  }

  // tslint:disable-next-line:max-line-length
  return p._id.toString().slice(-2) + `_` + getNum(getMD5(p.name)) + getNum(getMD5(p.brand)) + getNum(getMD5(p.series)) + getNum(getMD5(p.model))
}
